#include "PDTSCUtil.h"
#include "gia.h"
#include "abc.h"

ABC_NAMESPACE_IMPL_START

/**
 * Outside functions
 */
#define XVS0 ABC_INIT_ZERO 
#define XVS1 ABC_INIT_ONE
#define XVSX ABC_INIT_DC

static inline void Abc_ObjSetXsim( Abc_Obj_t * pObj, int Value )  { pObj->pCopy = (Abc_Obj_t *)(ABC_PTRINT_T)Value;  }
static inline int  Abc_ObjGetXsim( Abc_Obj_t * pObj )             { return (int)(ABC_PTRINT_T)pObj->pCopy;           }
static inline int  Abc_XsimInv( int Value )   
{ 
    if ( Value == XVS0 )
        return XVS1;
    if ( Value == XVS1 )
        return XVS0;
    assert( Value == XVSX );       
    return XVSX;
}
static inline int  Abc_XsimAnd( int Value0, int Value1 )   
{ 
    if ( Value0 == XVS0 || Value1 == XVS0 )
        return XVS0;
    if ( Value0 == XVSX || Value1 == XVSX )
        return XVSX;
    assert( Value0 == XVS1 && Value1 == XVS1 );
    return XVS1;
}
static inline int  Abc_XsimRand2()   
{
//    return (rand() & 1) ? XVS1 : XVS0;
    return (Gia_ManRandom(0) & 1) ? XVS1 : XVS0;
}
static inline int  Abc_XsimRand3()   
{
    int RetValue;
    do { 
//        RetValue = rand() & 3; 
        RetValue = Gia_ManRandom(0) & 3; 
    } while ( RetValue == 0 );
    return RetValue;
}
static inline int  Abc_ObjGetXsimFanin0( Abc_Obj_t * pObj )       
{ 
    int RetValue;
    RetValue = Abc_ObjGetXsim(Abc_ObjFanin0(pObj));
    return Abc_ObjFaninC0(pObj)? Abc_XsimInv(RetValue) : RetValue;
}
static inline int  Abc_ObjGetXsimFanin1( Abc_Obj_t * pObj )       
{ 
    int RetValue;
    RetValue = Abc_ObjGetXsim(Abc_ObjFanin1(pObj));
    return Abc_ObjFaninC1(pObj)? Abc_XsimInv(RetValue) : RetValue;
}
static inline void Abc_XsimPrint( FILE * pFile, int Value )   
{ 
    if ( Value == XVS0 )
    {
        fprintf( pFile, "0" );
        return;
    }
    if ( Value == XVS1 )
    {
        fprintf( pFile, "1" );
        return;
    }
    assert( Value == XVSX );       
    fprintf( pFile, "x" );
}

void PDTAbc_GetIo(string * pStrIn, string * pStrOut, string * pStrLatch, Abc_Ntk_t * pNtk) {
    if (pStrIn == NULL || pStrOut == NULL)
	return;

    if (pNtk == NULL) {
	cerr << "Network is NULL! Exiting" << endl;
	return;
    }

    int i;
    Abc_Obj_t * pObj;
    Abc_NtkForEachPi(pNtk, pObj, i)
	pStrIn[i] = Abc_ObjName(pObj);

    Abc_NtkForEachPo(pNtk, pObj, i)
	pStrOut[i] = Abc_ObjName(pObj);

    Abc_NtkForEachLatch(pNtk, pObj, i)
	pStrLatch[i] = Abc_ObjName(pObj);

    return;
}

void PDTAbc_NtkXValueSimulate(Abc_Ntk_t * pNtk, int nFrame, multimap<string, int> & mapPi, multimap< string, vector<int> > & mapPo, multimap<string, int> & mapLatch) {
    Abc_Obj_t * pObj;
    assert(Abc_NtkIsStrash(pNtk));

    Gia_ManRandom(1);

    Abc_ObjSetXsim(Abc_AigConst1(pNtk), XVS1);

    int i;
    Abc_NtkForEachPi(pNtk, pObj,i)
	Abc_ObjSetXsim(pObj, Abc_XsimRand2());

    Abc_NtkForEachLatch(pNtk, pObj,i)
	Abc_ObjSetXsim(Abc_ObjFanout0(pObj), Abc_LatchInit(pObj));

    // simulate
    cout << "======= Starting XSIM ======" << endl;
    for (int f = 0; f < nFrame; f++) {
        Abc_AigForEachAnd(pNtk, pObj, i)
            Abc_ObjSetXsim(pObj, Abc_XsimAnd(Abc_ObjGetXsimFanin0(pObj), Abc_ObjGetXsimFanin1(pObj)));

        Abc_NtkForEachCo(pNtk, pObj, i)
            Abc_ObjSetXsim(pObj, Abc_ObjGetXsimFanin0(pObj));

        Abc_NtkForEachPi(pNtk, pObj, i) {
            string name = Abc_ObjName(pObj);
            int val = Abc_ObjGetXsim(pObj);
            if (val == XVS0)
                val = 0;
            else if (val == XVS1)
                val = 1;
            mapPi.insert(pair<string, int>(name, val));
            //mapPi[name] = Abc_ObjGetXsim(pObj);
        }

        Abc_NtkForEachLatch(pNtk, pObj, i) {
            string name = Abc_ObjName(pObj);
            int val = Abc_ObjGetXsim(Abc_ObjFanout0(pObj));
            if (val == XVS0)
                val = 0;
            else if (val == XVS1)
                val = 1;
            mapLatch.insert(pair<string, int>(name, val));
            //mapLatch[name] = Abc_ObjGetXsim(Abc_ObjFanout0(pObj));
        }

        string prev_name = "";
        map< string, vector<int> > values;
        map< string, vector<int> >::iterator it;
        Abc_NtkForEachPo(pNtk, pObj, i) {
            string name = Abc_ObjName(pObj);
            it = values.find(name);
            vector <int> * vec = NULL;
            if (it == values.end()) {
                vector <int> tmp;
                values[name] = tmp;
                vec = &(values[name]);
            } else {
                vec = &((*it).second);
            }

            int val = Abc_ObjGetXsim(pObj);
            if (val == XVS0)
                val = 0;
            else if (val == XVS1)
                val = 1;

            vec->push_back(val);
            //mapPo.insert(pair<string, int>(name, val));
            //mapPo[name] = Abc_ObjGetXsim(pObj);
        }
        for (it = values.begin(); it != values.end(); it++) {
            mapPo.insert(pair< string, vector<int> >((*it).first, (*it).second));
        }
        values.clear();

        // assign input values
        Abc_NtkForEachPi(pNtk, pObj, i)
            Abc_ObjSetXsim(pObj, Abc_XsimRand2());

        // transfer the latch values
        Abc_NtkForEachLatch(pNtk, pObj, i)
            Abc_ObjSetXsim(Abc_ObjFanout0(pObj), Abc_ObjGetXsim(Abc_ObjFanin0(pObj)));

    }
    cout << "========= XSIM Ended =========" << endl;
}
ABC_NAMESPACE_IMPL_END
